﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reactive.Linq;
using Wulong.Phone.Utils.HttpHelpers;
using HtmlAgilityPack;

namespace Wulong.Phone.Qiushi.Models
{
    public static class QiushiMapper
    {
        //private const string YqlUrl = "http://query.yahooapis.com/v1/public/yql?q=";
        //private const string YqlEnv = "&env=store://datatables.org/alltableswithkeys";
        private const string BaseUrl = "http://www.qiushibaike.com";


        public static IObservable<QiushiComment> GetObservableComment(int id)
        {
            const string commentsQuery = BaseUrl + "/new2/comment/{0}";
            return Observable.Return((HttpWebRequest) WebRequest.Create(new Uri(string.Format(commentsQuery, id))))
                .SelectMany(request => Observable.FromAsyncPattern<WebResponse>(request.BeginGetResponse,
                                                                                request.EndGetResponse)())
                .SelectMany(response => GetComments(response.GetResponseStream()).ToObservable())
                //.Catch<QiushiComment, Exception>(e =>
                //{
                //    throw new Exception(e.Message);
                //})
                ;
        }

        private static IEnumerable<QiushiComment> GetComments(Stream stream)
        {
            var doc = new HtmlDocument();
            doc.Load(stream);
            var nodes = from n in doc.DocumentNode.Descendants("div")
                        where
                            n.HasAttributes && n.Attributes[0].Value.Equals("clearfix")
                        select n;
            return nodes.Select(BuildQiushiComment);
        }

        private static QiushiComment BuildQiushiComment(HtmlNode node)
        {
            var item = new QiushiComment();
            var commentNodes = node.Elements("div");
            foreach (var commentNode in commentNodes)
            {
                if (commentNode.Attributes["class"].Value.Equals("avatar"))
                {
                    item.Avatar = commentNode.Element("img").Attributes["src"].Value;
                }
                if (commentNode.Attributes["class"].Value.Equals("reply"))
                {
                    item.Author = commentNode.Element("a").InnerText;
                    item.Body = commentNode.Element("span").InnerText;
                }
                if (commentNode.Attributes["class"].Value.Equals("report"))
                {
                    item.Floor = commentNode.Element("span").InnerText + "L";
                }
            }
            return item;
        }



        public static IObservable<QiushiItem> GetObservableItems(string tab = "late", int itemsPerPage = 20,
                                                                 int pageNumber = 1)
        {
            const string naviUrl = BaseUrl + "/new2/{0}/{1}/page/{2}";
            //const string yqlQuery = "select * from html where url=\"" + naviUrl + "\" and xpath=\"{3}\"";
            //const string qiushiQueryUrl = YqlUrl + yqlQuery + YqlEnv;
            return
                GetObservableItems(
                    new Uri(String.Format(naviUrl, tab, itemsPerPage, pageNumber)));
        }

        public static IObservable<QiushiItem> GetObservableItems(Uri uri)
        {
            return Observable.Return((HttpWebRequest) WebRequest.Create(uri))
                .SelectMany(request => Observable.FromAsyncPattern<WebResponse>(
                    request.BeginGetResponse, request.EndGetResponse)())
                //.Catch<WebResponse, Exception>(e =>
                //                                       {
                //                                           throw new Exception(e.Message);   
                //                                       })
                .SelectMany(response => GetItem(response.GetResponseStream()).ToObservable())
                //.Catch<QiushiItem, Exception>(e =>
                //                                  {
                //                                      throw new Exception(e.Message);                                                     
                //                                  })
                ;  
                
        }

        private static IEnumerable<QiushiItem> GetItem(Stream stream)
        {
            var doc = new HtmlDocument();
            doc.Load(stream);
            var nodes = from n in doc.DocumentNode.Descendants("div")
                        where
                            n.HasAttributes && n.Attributes[0].Value.Equals("block untagged") 
                        select n;
            return nodes.Select(BuildQiushiItem);
        }

        private static QiushiItem BuildQiushiItem(HtmlNode node)
        {
            //remove "qiushi_tag_"
            var item = new QiushiItem { Id = int.Parse(node.Attributes["id"].Value.Substring(11)), PublishDateTime = DateTime.Parse(node.Attributes["title"].Value) };
            var children = node.Elements("div");
            foreach (var child in children)
            {
                if (child.Attributes[0].Value.Equals("content"))
                    item.Content = child.InnerText;
                if (child.Attributes[0].Value.Equals("thumb"))
                    item.ImageUrl = child.Element("img").Attributes["src"].Value.Replace("medium", "small");
                if (child.Attributes[0].Value.Equals("tags"))
                    foreach (var tagNode in child.Elements("a"))
                        item.Tags += tagNode.InnerText + " ";
                if (child.Attributes[0].Value.Equals("bar"))
                {
                    var temp = child.Element("div").InnerText.Split('\n');
                    item.Tops = int.Parse(temp[4]);//2 if yql
                    item.Flops = Math.Abs(int.Parse(temp[8]));//3 if yql
                    int commmentCount;
                    var isNumber = int.TryParse(temp[13].Substring(0, temp[13].Count()-3), out commmentCount);//temp[4].Substring(4, 1) if yql
                    item.CommentsCount = isNumber ? commmentCount : 0;
                }
            }
            return item;
        }

        public static IObservable<string> PostQiushiArticle(Dictionary<string, object> postParameters)
        {
            const string postUrl = "http://posttestserver.com/post.php";//BaseUrl + "/new2/add";
            const string userAgent =
                "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; BOIE9;ZHCN)";
            return MultipartFormDataPost(postUrl, userAgent, postParameters);
        }

        public static IObservable<string> MultipartFormDataPost(string postUrl, string userAgent, Dictionary<string, object> postParameters)
        {
            string formDataBoundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");//"---------------------------7dbe4045032c"; //"-----------------------------28947758029299";
                                             
            string contentType = "multipart/form-data; boundary=" + formDataBoundary;

            byte[] formData = PostHelper.GetMultipartFormData(postParameters, formDataBoundary);

            return PostForm(postUrl, userAgent, contentType, formData);
        }


        private static IObservable<string> PostForm(string postUrl, string userAgent, string contentType, byte[] formData)
        {
            var request = (HttpWebRequest)WebRequest.Create(new Uri(postUrl));//test with "http://posttestserver.com/post.php"
            request.Method = "POST";
            request.ContentType = contentType;
            request.UserAgent = userAgent;
            request.CookieContainer = new CookieContainer();

            var fetchRequestStream = Observable.FromAsyncPattern<Stream>(request.BeginGetRequestStream,
                                                                         request.EndGetRequestStream);
            var fetchResponse = Observable.FromAsyncPattern<WebResponse>(request.BeginGetResponse,
                                                                         request.EndGetResponse);
            return fetchRequestStream()
                .SelectMany(stream =>
                            {
                                using (var writer = new StreamWriter(stream))
                                    writer.Write(formData);
                                
                                return fetchResponse();
                            })
                .Select(result =>
                            {
                                var response = (HttpWebResponse)result;
                                return GetPostResponse(response.GetResponseStream());
                            })
                .Catch<string, Exception>(e=> Observable.Return(e.Message))
                .ObserveOnDispatcher();
        }

        private static string GetPostResponse(Stream stream)
        {
            var doc = new HtmlDocument();
            doc.Load(stream);
            var nodes =from n in doc.DocumentNode.Descendants("div")
                        where
                            n.HasAttributes && n.Attributes[0].Value.Equals("tagcloud")
                        select n.InnerText;
            return nodes.FirstOrDefault();
        }
    }
}